﻿using System;
using System.Diagnostics;
using System.IO;
using NMock2.Monitoring;

namespace NMock2.Events
{
	/// <summary>
	/// Can mock invoke any event it is feed with.
	/// <para><span style="color:red;font-weight:bold;">This class is new to NMock3.</span></para>
	/// </summary>
	/// <remarks>
	/// Code by Magnus Mårtensson
	/// http://blog.noop.se/archive/2007/08/17.aspx
	/// </remarks>
	public class MockEventInvoker<TEventArgs> where TEventArgs : EventArgs
	{
		private EventHandler<TEventArgs> _handler;

		/// <summary>
		/// Hooks up with the action that will be taken once a handler is added to the event.
		/// </summary>
		/// <returns>The action to hook the incoming handler to the event.</returns>
		[DebuggerStepThrough]
		public IAction Hookup()
		{
			return new MockEventHookup<TEventArgs>(this);
		}

		/// <summary>
		/// Invoke the event but do not care about the parameters.
		/// </summary>
		[DebuggerStepThrough]
		public void Invoke()
		{
			Invoke(null, null);
		}

		/// <summary>
		/// Invoke the event and send in parameter.
		/// </summary>
		/// <param name="sender">The sender of the event.</param>
		/// <param name="e">The arguments of the call.</param>
		[DebuggerStepThrough]
		public void Invoke(object sender, TEventArgs e)
		{
			if (_handler != null)
				_handler.Invoke(sender, e);
		}


		#region private


		private void Initialize(EventHandler<TEventArgs> handler)
		{
			_handler = handler;
		}

		private class MockEventHookup<TEventArgsInner> : IAction where TEventArgsInner : EventArgs
		{
			private readonly MockEventInvoker<TEventArgsInner> mockEventInvoker;


			/// <summary>
			/// downlaoded
			/// </summary>
			/// <param name="mockEventInvoker"></param>
			public MockEventHookup(MockEventInvoker<TEventArgsInner> mockEventInvoker)
			{
				this.mockEventInvoker = mockEventInvoker;
			}


			/// <summary>
			/// Downloaded
			/// </summary>
			/// <param name="invocation"></param>
			public void Invoke(Invocation invocation)
			{
				EventHandler<TEventArgsInner> handler = invocation.Parameters[0] as EventHandler<TEventArgsInner>;

				if (handler == null)
					throw new ArgumentNullException(string.Concat(@"invocation.", @"Parameters[0]"), string.Format("Event handler parameter is of the wrong type: {0}! EventHandler type expected!", invocation.Parameters[0].GetType()));

				mockEventInvoker.Initialize(handler);
			}

			/// <summary>
			/// Downloaded
			/// </summary>
			/// <param name="writer"></param>
			public void DescribeTo(TextWriter writer)
			{
				writer.Write("hook up a MockEventInvoker handler which can be invoked to fire the delegate method presented!");
			}
		}
		#endregion
	}
}
